import ast
import hashlib
import math
import os
import random
import re
import string
import threading
import time
import urllib

import jsonpickle
import requests
from bs4 import BeautifulSoup
from django.conf import settings
from django.core.paginator import EmptyPage, PageNotAnInteger, Paginator
from django.db.models import Q
from django.http import HttpRequest, HttpResponse, HttpResponseRedirect
from django.shortcuts import render

# from .function import add_errormsg_to_url, move_errormsg_in_url, update_a_tactical_text, reply_notification, time_to_datetime, get_ip, now
from . import creeptest, function
from .baidu_translate import translate_premium
from .models import (AccountUser, CommentsUser, InvitationCode, ReplyComment,
                     RequestHistory, Structure, Tactic)

# Create your views here.

# def tactical_text(request):
#     tacticals = Structure.objects.all()
#     for tactical in tacticals:
#         tacs = Tactic.objects.filter(structure=tactical, url__contains ='html')
#         for tac in tacs:
#             resu = get_a_text(tac.url[7:])
#             tac.synopsis = resu['SYNOPSIS']
#             tac.description = resu['DESCRIPTION']
#             tac.failure = resu['FAILURE']
#             tac.example = resu['EXAMPLE']
#             tac.comments = resu['COMMENTS']
#             tac.uses = resu['USES']
#             tac.seealso = resu['SEEALSO']
#             tac.save()
#     return HttpResponse('over')


def wordlen(request):
    tacticals = Tactic.objects.filter(tid__lte=6635, url__contains='html')
    wordlen = 0
    for tac in tacticals:
        if wordlen < 2000000:
            if tac.synopsis:
                wordlen += len(tac.synopsis)
            if tac.description:
                wordlen += len(tac.description)
            if tac.failure:
                wordlen += len(tac.failure)
            if tac.example:
                wordlen += len(tac.example)
            if tac.comments:
                wordlen += len(tac.comments)
            if tac.uses:
                wordlen += len(tac.uses)
        else:
            print('last tac:%d' % tac.tid, tac)
            return HttpResponse(tac)
    return HttpResponse(wordlen)
    # 总字符数：831840


def translate(request, silence=False, name='', stru=''):
    # error : tid 4104 4132
    if request:
        name = request.GET.get('name', '')
        stru = request.GET.get('stru', '')
    #silence = request.GET.get('silence', None)
    if name and stru:
        function.update_a_tactical_text(name, stru)
        tacticals = Tactic.objects.filter(name=name, structure=stru)
        # tacticals = Tactic.objects.filter(name='WF_REL_TAC', structure='bossLib')
        # print(len(tacticals))
        for tac in tacticals:
            if tac.type == "thm":
                continue
            if tac.synopsis:
                if "No official synopsis." in tac.synopsis:
                    continue
                tac.synopsis_cn = translate_premium(tac.synopsis)
            else:
                tac.synopsis_cn = None
            if tac.description:
                tac.description_cn = translate_premium(tac.description)
            else:
                tac.description_cn = None
            if tac.failure:
                tac.failure_cn = translate_premium(tac.failure)
            else:
                tac.failure_cn = None
            if tac.example:
                tac.example_cn = translate_premium(tac.example)
            else:
                tac.example_cn = None
            if tac.comments:
                tac.comments_cn = translate_premium(tac.comments)
            else:
                tac.comments_cn = None
            if tac.uses:
                tac.uses_cn = translate_premium(tac.uses)
            else:
                tac.uses_cn = None
            tac.updatever = creeptest.CREEP_VER
            tac.save()
        if silence:
            if tacticals.exists():
                return True
            else:
                return False
        return HttpResponseRedirect('/tactic/?name=%s&stru=%s' % (urllib.parse.quote(name), urllib.parse.quote(stru)))
    if silence:
        return False
    return render(request, 'structure.html', {'fromDB': tacticals})


def test(request):
    msg = request.GET.get('msg', '')
    tac = Tactic.objects.get(name='add_rule', structure='Parse')
    print(tac._meta.fields)
    print(tac.__dict__)
    return HttpResponse('test:%s' % msg)


def save_request_history(request, user):
    try:
        ip = function.get_ip(request)
        address = "".join(function.query_ipaddress(ip))
        a = RequestHistory(ip=ip, time=function.now(),
                           user=user, address=address)
        a.save()
    except Exception as e:
        print("save_request_history error:%s" % str(e))
        a = RequestHistory(time=function.now(), user=user, address=str(e))
        a.save()
    return True


def search_index(request):
    user = request.session.get('login', None)
    if user:
        user = AccountUser.objects.get(uid=jsonpickle.loads(user).uid)
        if user.admin == 1:
            request_history = RequestHistory.objects.all().order_by('-time')
    # save_request_history(request,user)
    t = threading.Thread(target=save_request_history, args=(request, user, ))
    t.start()
    #threading._start_new_thread(save_request_history, (request, user, ))
    return render(request, 'search_index.html', locals())


def search_result(request):
    user = request.session.get('login', None)
    if user:
        user = AccountUser.objects.get(uid=jsonpickle.loads(user).uid)
    q = request.GET.get('q', '')
    n = int(request.GET.get('n', '1'))
    size = int(request.GET.get('pagesize', '20'))
    if q:
        if q == '*':
            tacs = Tactic.objects.all()
        else:
            # tacs = Tactic.objects.filter(name__contains=q)
            tacs = Tactic.objects.filter(Q(name__icontains=q) | Q(structure__name__icontains=q) | Q(description__icontains=q) | Q(comments__icontains=q) | Q(example__icontains=q) | Q(uses__icontains=q) | Q(uses_cn__icontains=q) | Q(
                description_cn__icontains=q) | Q(comments_cn__icontains=q) | Q(example_cn__icontains=q) | Q(failure__icontains=q) | Q(failure_cn__icontains=q) | Q(synopsis__icontains=q) | Q(synopsis_cn__icontains=q)).distinct()
        stru_count = {}
        for stru in Structure.objects.all():
            stru_count[stru.name] = 0
        for count, tac in enumerate(tacs):
            stru_count[tac.structure.name] += 1
        stru_count = sorted(stru_count.items(),
                            key=lambda x: x[1], reverse=True)
        distribution = []
        for x in stru_count:
            if x[1] != 0:
                distribution.append({'stru': Structure.objects.get(
                    name=x[0]), 'value': format(x[1]/len(tacs), '.0%'), 'num': x[1]})
        # for tac in tacs:
        #     tac.name = tac.name.replace(q, '<em>%s<em>'%q)
        #     print(type(tac.name) ,tac.name)
        if distribution == []:
            distribution = None
        # totalnum = len(tacs)
        # totalPages = int(math.ceil(totalnum/size))
        # if n < 1: n = 1
        # if n > totalPages: n = totalPages
        # ntacs = tacs[((n-1)*size):(n*size)]
        onlyshow = request.GET.get('onlyshow', None)
        if onlyshow:
            tacs = tacs.filter(structure__name=onlyshow)
            tacs = list(Tactic.objects.filter(Q(name__icontains=q)
                                              & Q(structure__name=onlyshow))) + list(tacs)
            tacs = sorted(set(tacs), key=tacs.index)
        else:
            if distribution != [] and tacs:
                tacs = function.QuerysSet_sort(
                    tacs, [x['stru'] for x in distribution])
                tacs = (list(Tactic.objects.filter(Q(name__icontains=q))) + tacs)
                tacs = sorted(set(tacs), key=tacs.index)
        pager = Paginator(tacs, size)
        try:
            perpage_data = pager.page(n)
        except PageNotAnInteger:
            perpage_data = pager.page(1)
        except EmptyPage:
            perpage_data = pager.page(pager.num_pages)
        for each in perpage_data:
            temp = CommentsUser.objects.filter(position=each, note=0)
            each.seealso = str(0)
            if temp.exists():
                for eachC in temp:
                    each.seealso = str(
                        int(each.seealso) + 1 + ReplyComment.objects.filter(position=eachC).count())
        if pager.num_pages < 10:
            showpagerange = [x for x in range(1, pager.num_pages+1)]
        else:
            if n < 5:
                showpagerange = [x for x in range(1, 10)]
            else:
                if n < pager.num_pages-4:
                    showpagerange = [x for x in range(n-4, n+5)]
                else:
                    showpagerange = [x for x in range(
                        pager.num_pages-8, pager.num_pages+1)]
        if distribution != None:
            if len(distribution) >= 54:
                distribution = distribution[:54]
        return render(request, 'search_result.html', {'title': '%s search result' % q, 'fromDB': perpage_data, 'distribution': distribution, 'result_num': len(tacs), 'showpagerange': showpagerange, 'pager': pager, 'q': q, 'user': user, 'onlyshow': onlyshow})
    else:
        return render(request, 'search_index.html', locals())


def tactic(request):
    user = request.session.get('login', None)
    name = request.GET.get('name', '')
    stru = request.GET.get('stru', '')
    if stru == 'Thm':
        stru = 'FinalThm'
    errormsg = request.GET.get('errormsg', None)
    if name and stru:
        note = None
        attributes = ['synopsis', 'description',
                      'failure', 'example', 'comments', 'uses']
        try:
            tac = Tactic.objects.get(name=name, structure=stru)
        except:
            if translate(None, True, name, stru):
                return HttpResponseRedirect(request.get_full_path())
            return render(request, '404.html', {'errormsg': '数据库中没有%s.%s' % (stru, name)})
        dic = tac.__dict__
        if dic['url'] != None:
            try:
                dic['seealso'] = ast.literal_eval(dic['seealso'])
                for key, value in dic['seealso'].items():
                    if key != 'Source page':
                        dic['seealso'][key] = '/tactic/?name=%s&stru=%s' % (urllib.parse.quote(os.path.basename(
                            value).split('.')[1]), urllib.parse.quote(os.path.basename(value).split('.')[0]))
            except Exception as e:
                print("seealso error: %s" % e)
                dic['seealso'] = {}
            if dic['type'] == 'thm':
                dic['seealso']['Source page'] = 'https://hol-theorem-prover.org/kananaskis-13-helpdocs/help/src-sml/htmlsigs/' + \
                    os.path.basename(tac.structure.url) + '#%s-val' % name
            else:
                dic['seealso']['Source page'] = 'https://hol-theorem-prover.org/kananaskis-13-helpdocs/help/Docfiles/HTML/' + \
                    os.path.basename(tac.url)
        for key, value in dic.items():
            if key in attributes and value != None and (dic[key+'_cn'] == None or dic[key+'_cn'] == ''):
                dic[key+'_cn'] = '翻译暂缺'
        if user:
            user = AccountUser.objects.get(name=jsonpickle.loads(user).name)
            if request.method == "POST":
                rtext = request.POST.get('rtext', None)
                if rtext:
                    cpositon = request.GET.get('p', None)
                    try:
                        cp = CommentsUser.objects.get(id=int(cpositon))
                    except Exception as e:
                        errormsg = "Quick reply faile:%s" % e
                        return render(request, '404.html', {'errormsg': errormsg})
                    if cpositon and not ReplyComment.objects.filter(user=user, content=rtext, position=cp).exists():
                        newR = ReplyComment(
                            user=user, position=cp, content=rtext, time=function.now())
                        newR.save()
                        function.reply_notification(newR)
                    return HttpResponseRedirect('/tactic/?name=%s&stru=%s' % (urllib.parse.quote(name), urllib.parse.quote(stru)))
                comment = request.POST.get('conmment-text', '')
                noteflag = int(request.GET.get('noteflag', '0'))
                old_note = CommentsUser.objects.filter(
                    position=tac, user=user, note=1)
                if comment and not old_note.exists() and not noteflag == 2:
                    aComment = CommentsUser(
                        position=tac, user=user, content=comment, time=function.now(), note=noteflag)
                    aComment.save()
                if comment and old_note.exists() and noteflag == 1:
                    old_note.update(content=comment, time=function.now())
                if comment == '' and old_note.exists() and noteflag == 1:
                    old_note.first().delete()
                if comment and old_note.exists() and noteflag == 0:
                    old_note.update(content=comment,
                                    time=function.now(), note=0)
                if noteflag == 1:
                    return HttpResponseRedirect('/tactic/?name=%s&stru=%s#markdown-text' % (urllib.parse.quote(name), urllib.parse.quote(stru)))
                if noteflag == 2:
                    if comment != '':
                        comment += '\n {*感谢用户 %s 提交该描述*}' % user.name
                    else:
                        comment = '{*该描述于<%s>被用户 %s 清除*}' % (time.strftime(
                            "%Y-%m-%d %H:%M:%S", time.localtime()), user.name)
                    if Tactic.objects.get(name=name, structure__name=stru).synopsis == None:
                        Tactic.objects.filter(name=name, structure__name=stru).update(
                            synopsis='No official synopsis.', synopsis_cn=comment)
                    else:
                        Tactic.objects.filter(name=name, structure__name=stru).update(
                            synopsis_cn=comment)
                    try:
                        old_note.first().delete()
                    except:
                        pass
                    return HttpResponseRedirect('/tactic/?name=%s&stru=%s' % (urllib.parse.quote(name), urllib.parse.quote(stru)))
            try:
                note = CommentsUser.objects.get(
                    position=tac, user=user, note=1)
            except:
                note = None
        coms = CommentsUser.objects.filter(position=tac, note=0)
        if coms.exists():
            for com in coms:
                replys = ReplyComment.objects.filter(position=com)
                com = com.__dict__
                com['username'] = AccountUser.objects.get(
                    uid=com['user_id']).name
                com['replys'] = replys
        return render(request, 'tactic.html', {'tac': dic, 'contentlist': attributes, 'user': user, 'coms': coms, 'note': note, 'errormsg': errormsg})
    return render(request, '404.html', {'user': user})


def replace(request):
    #attributes = ['SYNOPSIS', 'DESCRIPTION', 'FAILURE', 'EXAMPLE', 'COMMENTS', 'USES']
    #tacs = Tactic.objects.filter(tid__gte=6635, structure=tactical, url__contains ='html')
    tacs = Tactic.objects.all()
    for tac in tacs:
        if tac.synopsis:
            #tac.synopsis_cn = translate_api(tac.synopsis)['trans_result'][0]['dst']
            tac.synopsis = tac.synopsis.replace('<*', '%*').replace('*>', '*%')
        if tac.description:
            # if len(tac.description) > 2000:
            #     tac.description_cn = translate_split(tac.description)
            # else:
            #     tac.description_cn = translate_api(tac.description)['trans_result'][0]['dst']
            tac.description = tac.description.replace(
                '<*', '%*').replace('*>', '*%')
        if tac.failure:
            # tac.failure_cn = translate_api(tac.failure)['trans_result'][0]['dst']
            tac.failure = tac.failure.replace('<*', '%*').replace('*>', '*%')
        if tac.example:
            # if len(tac.example) > 2000:
            #     tac.example_cn = translate_split(tac.example_cn)
            # else:
            #     tac.example_cn = translate_api(tac.example)['trans_result'][0]['dst']
            tac.example = tac.example.replace('<*', '%*').replace('*>', '*%')
        if tac.comments:
            # tac.comments_cn = translate_api(tac.comments)['trans_result'][0]['dst']
            tac.comments = tac.comments.replace('<*', '%*').replace('*>', '*%')
        if tac.uses:
            # tac.uses_cn = translate_api(tac.uses)['trans_result'][0]['dst']
            tac.uses = tac.uses.replace('<*', '%*').replace('*>', '*%')
        tac.save()
    return HttpResponse(len(tacs))


def structure(request):
    user = request.session.get('login', None)
    if user:
        user = AccountUser.objects.get(uid=jsonpickle.loads(user).uid)
    stru = request.GET.get('q', '')
    if stru == 'Thm':
        stru = 'FinalThm'
    n = int(request.GET.get('n', '1'))
    size = int(request.GET.get('size', '20'))
    if stru:
        tacs = Tactic.objects.filter(structure=stru)
        pager = Paginator(tacs, size)
        try:
            perpage_data = pager.page(n)
        except PageNotAnInteger:
            perpage_data = pager.page(1)
        except EmptyPage:
            perpage_data = pager.page(pager.num_pages)
        for each in perpage_data:
            temp = CommentsUser.objects.filter(position=each, note=0)
            each.seealso = str(0)
            if temp.exists():
                for eachC in temp:
                    each.seealso = str(
                        int(each.seealso) + 1 + ReplyComment.objects.filter(position=eachC).count())
        if pager.num_pages < 10:
            showpagerange = [x for x in range(1, pager.num_pages+1)]
        else:
            if n < 5:
                showpagerange = [x for x in range(1, 10)]
            else:
                if n < pager.num_pages-4:
                    showpagerange = [x for x in range(n-4, n+5)]
                else:
                    showpagerange = [x for x in range(
                        pager.num_pages-8, pager.num_pages+1)]
        # return render(request, 'structure.html', {'title': stru, 'fromDB':tacs})
        return render(request, 'structure.html', {'title': '%s Structure' % stru, 'fromDB': perpage_data, 'result_num': len(tacs), 'showpagerange': showpagerange, 'pager': pager, 'q': stru, 'user': user})
    return render(request, '404.html', locals())


def salt_generator(n):
    chars = string.ascii_letters + string.digits
    return "".join(random.choice(chars) for _ in range(n))
# copy from : https://blog.csdn.net/Jerry_1126/article/details/86726823


def register(request):
    user = request.session.get('login', None)
    if user:
        user = AccountUser.objects.get(uid=jsonpickle.loads(user).uid)
        return render(request, 'register.html', {'user': user})
    if request.method == 'POST':
        try:
            url = request.get_full_path().split('url=')[1]
        except:
            url = '/'
        name = request.POST.get('uname', None)
        pwd = request.POST.get('upwd', None)
        rpwd = request.POST.get('rpwd', None)
        invitationCode = request.POST.get('icode', None)
        email = request.POST.get('email', None)
        if name and pwd and invitationCode:
            icodeL = InvitationCode.objects.filter(code=invitationCode)
            if icodeL.count() > 0:
                icode = icodeL.first()
                if icode.margin > 0:
                    if pwd == rpwd:
                        if not AccountUser.objects.filter(name=name).exists():
                            if not AccountUser.objects.filter(email=email).exists():
                                salt = salt_generator(32)
                                while AccountUser.objects.filter(salt=salt).exists():
                                    salt = salt_generator(32)
                                try:
                                    pwds = hashlib.sha256(
                                        (hashlib.sha256(pwd.encode()).hexdigest()+salt).encode()).hexdigest()
                                    auser = AccountUser(name=name, pwd=pwds, salt=salt, group=";%s's Favorites;@;%s;" % (
                                        name, invitationCode), email=email, acceptemail=0, verifyemail=0)
                                    auser.save()
                                    icode.margin -= 1
                                    icode.save()
                                    request.session['login'] = jsonpickle.dumps(
                                        auser)
                                    return HttpResponseRedirect('/user/index/?ecode=4')
                                except Exception as e:
                                    errormsg = e
                            else:
                                errormsg = "邮箱已经被注册"
                        else:
                            errormsg = "用户名已被注册"
                    else:
                        errormsg = "密码不相同"
                else:
                    errormsg = "邀请码不可用"
            else:
                errormsg = "邀请码不可用"
        else:
            errormsg = "用户名、密码、邀请码 皆不能为空"
        return render(request, 'register.html', {'errormsg': errormsg, 'user': None})
    else:
        return render(request, 'register.html', {'user': None})


def login(request):
    user = request.session.get('login', None)
    errorcode = request.GET.get('ecode', None)
    if errorcode:
        errormsg = settings.ERROR_DIC[errorcode]
    else:
        errormsg = None
    if user:
        user = request.session['login']
        return render(request, 'login.html', {'user': jsonpickle.loads(user)})
    if request.method == 'POST':
        try:
            url = request.get_full_path().split('url=')[1]
            if url == '':
                url = '/community/group'
        except:
            url = '/'
        name = request.POST.get('uname', '')
        pwd = request.POST.get('upwd', '')
        keep = request.POST.get('keep', '')
        if name and pwd:
            users = AccountUser.objects.filter(name=name)
            if users.exists():
                try:
                    user = users.first()
                    if hashlib.sha256((hashlib.sha256(pwd.encode()).hexdigest()+user.salt).encode()).hexdigest() == user.pwd:
                        request.session['login'] = jsonpickle.dumps(user)
                        if keep == '':
                            request.session.set_expiry(0)
                        return HttpResponseRedirect(url)
                    else:
                        errormsg = "密码错误"
                except Exception as e:
                    errormsg = e
            else:
                errormsg = "该用户不存在"
        else:
            errormsg = "请输入用户名和密码"
        user = None
        return render(request, 'login.html', {'errormsg': errormsg, 'user': user})
    user = None
    return render(request, 'login.html', {'user': user, 'errormsg': errormsg})


def logout(request):
    request.session.flush()
    try:
        url = request.get_full_path().split('purl=')[1]
    except:
        url = '/'
    return HttpResponseRedirect(url)


def redirect(request):
    try:
        url = request.get_full_path().split('purl=')[1].split('url=')[1]
    except:
        url = '/'
    return HttpResponseRedirect(url)


def user_comment(request):
    user = request.session.get('login', None)
    n = request.GET.get('n', '1')
    errormsg = request.GET.get('errormsg', None)
    if user:
        user = request.session['login']
        user = AccountUser.objects.get(uid=jsonpickle.loads(user).uid)
        coms = CommentsUser.objects.filter(user=user).order_by('-time')
        if coms.exists():
            for com in coms:
                replys = ReplyComment.objects.filter(position=com)
                com = com.__dict__
                com['username'] = AccountUser.objects.get(
                    uid=com['user_id']).name
                com['replys'] = replys
        pager = Paginator(coms, 20)
        try:
            perpage_data = pager.page(n)
        except PageNotAnInteger:
            perpage_data = pager.page(1)
        except EmptyPage:
            perpage_data = pager.page(pager.num_pages)
        if pager.num_pages < 10:
            showpagerange = [x for x in range(1, pager.num_pages+1)]
        else:
            if n < 5:
                showpagerange = [x for x in range(1, 10)]
            else:
                if n < pager.num_pages-4:
                    showpagerange = [x for x in range(n-4, n+5)]
                else:
                    showpagerange = [x for x in range(
                        pager.num_pages-8, pager.num_pages+1)]
        return render(request, 'user_comments.html', {'fromDB': perpage_data, 'pager': pager, 'showpagerange': showpagerange, 'errormsg': errormsg, 'user': user})
    else:
        return HttpResponseRedirect('/login?url=/user/comment')


def comment_delete(request):
    user = request.session.get('login', None)
    cid = request.GET.get('id', '')
    t = request.GET.get('t', None)
    try:
        url = request.get_full_path().split('url=')[1]
    except Exception as e:
        url = '/'
    if user:
        user = request.session['login']
        user = AccountUser.objects.get(uid=jsonpickle.loads(user).uid)
        switch_DB = {'c': CommentsUser, 'qr': ReplyComment}
        if cid and t in switch_DB.keys():
            com = switch_DB[t].objects.get(id=int(cid))
            if user == com.user or user.uid == com.user.uid:
                com.delete()
    return HttpResponseRedirect(url)


def comment_change_statu(request):
    user = request.session.get('login', None)
    cid = request.GET.get('id', '')
    note = request.GET.get('note', '')
    try:
        url = request.get_full_path().split('url=')[1]
        url = function.move_errormsg_in_url(url)
    except Exception as e:
        url = '/'
    if user:
        user = request.session['login']
        user = AccountUser.objects.get(uid=jsonpickle.loads(user).uid)
        if cid and note:
            com = CommentsUser.objects.filter(id=cid)
            if int(note) == 1 and CommentsUser.objects.filter(position=com.first().position, user=user, note=1).exists():
                pos = com.first().position.structure.name + '.' + com.first().position.name
                errormsg = '转存失败，%s中存在未发表或清空的笔记' % pos
                url = function.add_errormsg_to_url(url, errormsg)
            elif user == com.first().user:
                com.update(note=note, time=function.now())
    return HttpResponseRedirect(url)

# def site_map(request):
#     with open('/home/satone/HOL4inWeb/static/sitemap/sitemap.xml','w') as f:
#         for each in Tactic.objects.all():
#             if each.structure.name == 'Thm':
#                 each.structure.name = 'FinalThm'
#             url = 'http://www.holreference.top/tactic/?name=%s&stru=%s\n'%(each.name, each.structure.name)
#             f.write('''<url>
#                 <loc>{}</loc>
#                 <priority>0.5</priority>
#                 <lastmod>2020-07-06T09:27:48+08:00</lastmod>
#                 <changefreq>weekly</changefreq>
#             </url>'''.format(url))
#     return HttpResponse('over')
